package io.github.cchenxi.crpc.core.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.jetbrains.annotations.Nullable;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * 类型工具类
 * Date: 2024-03-15
 *
 * @author chenxi
 */
public final class TypeUtil {
    public static Object cast(Object originData, Class<?> returnType) {
        if (Objects.isNull(originData)) {
            return null;
        }
        Class<?> originClazz = originData.getClass();
        if (returnType.isAssignableFrom(originClazz)) {
            return originData;
        }

        if (returnType.isArray()) {
            if (originData instanceof List list) {
                originData = list.toArray();
            }
            int length = Array.getLength(originData);
            Class<?> componentType = returnType.getComponentType();
            Object resultArray = Array.newInstance(componentType, length);
            for (int i = 0; i < length; i++) {
                Array.set(resultArray, i, Array.get(originData, i));
            }
            return resultArray;
        }

        if (originData instanceof HashMap map) {
            JSONObject jsonObject = new JSONObject(map);
            return jsonObject.toJavaObject(returnType);
        }

        if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
            return Long.valueOf(originData.toString());
        } else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
            return Integer.valueOf(originData.toString());
        } else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE)) {
            return Float.valueOf(originData.toString());
        } else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE)) {
            return Double.valueOf(originData.toString());
        } else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
            return Short.valueOf(originData.toString());
        } else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE)) {
            return Byte.valueOf(originData.toString());
        } else if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
            return Boolean.valueOf(originData.toString());
        } else if (returnType.equals(Character.class) || returnType.equals(Character.TYPE)) {
            return originData.toString().charAt(0);
        }
        return null;
    }

    @Nullable
    public static Object castMethodResult(Method method, Object data) {
        Class<?> returnType = method.getReturnType();
        System.out.println("method returnType is " + returnType);

        if (data instanceof JSONObject jsonObject) {
            if (Map.class.isAssignableFrom(returnType)) {
                Map resultMap = new HashMap();
                Type genericReturnType = method.getGenericReturnType();
                System.out.println(genericReturnType);
                if (genericReturnType instanceof ParameterizedType parameterizedType) {
                    Class<?> keyType = (Class<?>)parameterizedType.getActualTypeArguments()[0];
                    Class<?> valueType = (Class<?>)parameterizedType.getActualTypeArguments()[1];
                    System.out.println("keyType  : " + keyType);
                    System.out.println("valueType: " + valueType);
                    jsonObject.entrySet().stream().forEach(
                            e -> {
                                Object key = cast(e.getKey(), keyType);
                                Object value = cast(e.getValue(), valueType);
                                resultMap.put(key, value);
                            }
                    );
                }
                return resultMap;
            }
            return jsonObject.toJavaObject(method.getReturnType());
        } else if (data instanceof JSONArray jsonArray) {
            Object[] array = jsonArray.toArray();
            if (returnType.isArray()) {
                Class<?> componentType = returnType.getComponentType();
                Object resultArray = Array.newInstance(componentType, array.length);
                for (int i = 0; i < array.length; i++) {
                    Array.set(resultArray, i, cast(array[i], componentType));
                }
                return resultArray;
            } else if (List.class.isAssignableFrom(returnType)) {
                List<Object> resultList = new ArrayList<>(array.length);
                Type genericReturnType = method.getGenericReturnType();
                System.out.println(genericReturnType);
                if (genericReturnType instanceof ParameterizedType parameterizedType) {
                    Type actualType = parameterizedType.getActualTypeArguments()[0];
                    System.out.println(actualType);
                    for (Object o : array) {
                        resultList.add(cast(o, (Class<?>) actualType));
                    }
                } else {
                    resultList.addAll(Arrays.asList(array));
                }
                return resultList;
            } else {
                return null;
            }
        } else {
            return cast(data, method.getReturnType());
        }
    }
}
